package com.zrx.zrxpicturebackend.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zrx.zrxpicturebackend.exception.BusinessException;
import com.zrx.zrxpicturebackend.exception.ErrorCode;
import com.zrx.zrxpicturebackend.exception.ThrowUtils;
import com.zrx.zrxpicturebackend.mapper.SpaceMapper;
import com.zrx.zrxpicturebackend.model.dto.space.SpaceAddRequest;
import com.zrx.zrxpicturebackend.model.entity.Space;
import com.zrx.zrxpicturebackend.model.entity.User;
import com.zrx.zrxpicturebackend.model.enums.SpaceLevelEnum;
import com.zrx.zrxpicturebackend.service.SpaceService;
import com.zrx.zrxpicturebackend.service.UserService;
import com.zrx.zrxpicturebackend.service.PictureService;
import com.zrx.zrxpicturebackend.model.entity.Picture;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.List;

/**
 * @author zrx
 * @description 针对表【space(空间)】的数据库操作Service实现
 * @createDate 2025-11-08 11:31:29
 */
@Service
@Slf4j
public class SpaceServiceImpl extends ServiceImpl<SpaceMapper, Space>
        implements SpaceService {

    @Resource
    private TransactionTemplate transactionTemplate;

    @Resource
    private UserService userService;
    
    @Resource
    private PictureService pictureService;

    @Resource
    private RedissonClient redissonClient;


    @Override
    public void validSpace(Space space, boolean add) {
        ThrowUtils.throwIf(space == null, ErrorCode.PARAMS_ERROR);
        // 从对象中取值
        String spaceName = space.getSpaceName();
        Integer spaceLevel = space.getSpaceLevel();
        SpaceLevelEnum spaceLevelEnum = SpaceLevelEnum.getEnumByValue(spaceLevel);
        // 要创建
        if (add) {
            if (StrUtil.isBlank(spaceName)) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "空间名称不能为空");
            }
            if (spaceLevel == null) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "空间级别不能为空");
            }
        }
        // 修改数据时，如果要改空间级别
        if (spaceLevel != null && spaceLevelEnum == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "空间级别不存在");
        }
        if (StrUtil.isNotBlank(spaceName) && spaceName.length() > 30) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "空间名称过长");
        }
    }

    @Override
    public void fillSpaceBySpaceLevel(Space space) {
        // 根据空间级别，自动填充限额
        SpaceLevelEnum spaceLevelEnum = SpaceLevelEnum.getEnumByValue(space.getSpaceLevel());
        if (spaceLevelEnum != null) {
            long maxSize = spaceLevelEnum.getMaxSize();
            if (space.getMaxSize() == null) {
                space.setMaxSize(maxSize);
            }
            long maxCount = spaceLevelEnum.getMaxCount();
            if (space.getMaxCount() == null) {
                space.setMaxCount(maxCount);
            }
        }
    }

    @Override
    public long addSpace(SpaceAddRequest spaceAddRequest, User loginUser) {
        // 在此处将实体类和 DTO 进行转换
        Space space = new Space();
        BeanUtils.copyProperties(spaceAddRequest, space);

        // 默认值设置
        if (StrUtil.isBlank(spaceAddRequest.getSpaceName())) {
            space.setSpaceName("默认空间");
        }
        if (spaceAddRequest.getSpaceLevel() == null) {
            space.setSpaceLevel(SpaceLevelEnum.COMMON.getValue());
        }

        // 填充数据
        this.fillSpaceBySpaceLevel(space);

        // 数据校验
        this.validSpace(space, true);

        // 获取要创建空间的用户ID
        Long userId;
        if (spaceAddRequest.getUserId() != null && userService.isAdmin(loginUser)) {
            // 管理员可以为指定用户创建空间
            userId = spaceAddRequest.getUserId();
        } else {
            // 普通用户只能为自己创建空间
            userId = loginUser.getId();
        }

        space.setUserId(userId);

        // 权限校验
        if (SpaceLevelEnum.COMMON.getValue() != spaceAddRequest.getSpaceLevel() && !userService.isAdmin(loginUser)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限创建指定级别的空间");
        }

        log.info("开始创建空间，userId: {}, spaceName: {}", userId, space.getSpaceName());

        // 使用 Redisson 分布式锁
        RLock lock = redissonClient.getLock("space_create_lock:" + userId);
        try {
            // 设置获取锁的超时时间和锁的持有时间，避免死锁
            if (!lock.tryLock(10, 30, TimeUnit.SECONDS)) {
                log.warn("获取空间创建锁超时，userId: {}", userId);
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "系统繁忙，请稍后重试");
            }

            Long newSpaceId = createSpaceInTransaction(space, userId);

            log.info("空间创建成功，spaceId: {}, userId: {}", newSpaceId, userId);

            // 返回结果是包装类，可以做一些处理
            return Optional.ofNullable(newSpaceId).orElse(-1L);

        } catch (TransactionException e) {
            log.error("创建空间事务失败，userId: {}", userId, e);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "创建空间失败");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("创建空间被中断，userId: {}", userId, e);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "操作被中断");
        } catch (Exception e) {
            log.error("创建空间异常，userId: {}", userId, e);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "系统异常");
        } finally {
            // 分布式锁会自动释放，但仍建议显式解锁
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 在事务中创建空间
     *
     * @param space  空间实体
     * @param userId 用户ID
     * @return 新创建的空间ID
     */
    private Long createSpaceInTransaction(Space space, Long userId) {
        return transactionTemplate.execute(status -> {
            boolean exists = this.lambdaQuery()
                    .eq(Space::getUserId, userId)
                    .exists();
            ThrowUtils.throwIf(exists, ErrorCode.OPERATION_ERROR, "每个用户仅能有一个私有空间");

            boolean result = this.save(space);
            ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
            return space.getId();
        });
    }
    
    @Override
    public void deleteSpace(Long spaceId, User loginUser) {
        // 参数校验
        ThrowUtils.throwIf(spaceId == null || spaceId <= 0, ErrorCode.PARAMS_ERROR, "空间ID不合法");
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NO_AUTH_ERROR);
        
        // 判断空间是否存在
        Space space = this.getById(spaceId);
        ThrowUtils.throwIf(space == null, ErrorCode.NOT_FOUND_ERROR, "空间不存在");
        
        // 校验权限：只有空间创建者或管理员可以删除空间
        if (!space.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限删除该空间");
        }
        
        log.info("开始删除空间，spaceId: {}, userId: {}", spaceId, loginUser.getId());
        
        // 使用 Redisson 分布式锁
        RLock lock = redissonClient.getLock("space_delete_lock:" + spaceId);
        try {
            // 设置获取锁的超时时间和锁的持有时间，避免死锁
            if (!lock.tryLock(10, 30, TimeUnit.SECONDS)) {
                log.warn("获取空间删除锁超时，spaceId: {}, userId: {}", spaceId, loginUser.getId());
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "系统繁忙，请稍后重试");
            }
            
            deleteSpaceInTransaction(spaceId, loginUser);
            
            log.info("空间删除成功，spaceId: {}, userId: {}", spaceId, loginUser.getId());
            
        } catch (TransactionException e) {
            log.error("删除空间事务失败，spaceId: {}, userId: {}", spaceId, loginUser.getId(), e);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "删除空间失败");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("删除空间被中断，spaceId: {}, userId: {}", spaceId, loginUser.getId(), e);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "操作被中断");
        } catch (Exception e) {
            log.error("删除空间异常，spaceId: {}, userId: {}", spaceId, loginUser.getId(), e);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "系统异常");
        } finally {
            // 分布式锁会自动释放，但仍建议显式解锁
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
    
    /**
     * 在事务中删除空间及其中的所有图片
     *
     * @param spaceId  空间ID
     * @param loginUser 当前登录用户
     */
    private void deleteSpaceInTransaction(Long spaceId, User loginUser) {
        transactionTemplate.execute(status -> {
            // 1. 查询空间中的所有图片
            List<Picture> pictureList = pictureService.lambdaQuery()
                    .eq(Picture::getSpaceId, spaceId)
                    .list();
            
            log.info("空间中包含 {} 张图片，开始删除", pictureList.size());
            
            // 2. 删除空间中的所有图片
            for (Picture picture : pictureList) {
                try {
                    pictureService.deletePicture(picture.getId(), loginUser);
                    log.debug("删除图片成功，pictureId: {}", picture.getId());
                } catch (Exception e) {
                    log.error("删除图片失败，pictureId: {}", picture.getId(), e);
                    // 继续删除其他图片，不因单个图片删除失败而中断整个流程
                }
            }
            
            // 3. 删除空间本身
            boolean result = this.removeById(spaceId);
            ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "删除空间失败");
            
            return true;
        });
    }

}